#include "rendPlatform.hpp"
#include "rendAttrBuffer.hpp"
#include "rendVect.hpp"
#include "rendVectorArray.hpp"
#include "rendMaterial.hpp"

#include <string>

#include "rendBoxVisual.hpp"

namespace rend
{

const std::string BoxVisual::s_vtx("+box_vtx");
const std::string BoxVisual::s_idx("+box_idx");

static const std::string attr_semantics_position("Vertex");
static const std::string attr_semantics_index("index");


void BoxVisual::draw()
{
	const Material::Program* prog = Material::prog;

	if (!prog)
	{
		fprintf(stderr, "%s: no gpu program to draw with; bailing out\n", __FUNCTION__);
		return;
	}

	const AttrBuffer16* attr_buffer;

	if (0 == (attr_buffer = AttrBuffer16::s_default_register.seek(s_vtx)) ||
		attr_buffer->getBufferType() != AttrBuffer16::BUFFER_TYPE_VERTEX)
	{
		static const vect3 __min(-1.f, -1.f, -1.f);
		static const vect3 __max(1.f, 1.f, 1.f);

		static const float __vtx[8][3] =
		{
			{ __max[0], __max[1], __max[2] },
			{ __min[0], __max[1], __max[2] },
			{ __max[0], __min[1], __max[2] },
			{ __max[0], __max[1], __min[2] },

			{ __min[0], __min[1], __min[2] },
			{ __max[0], __min[1], __min[2] },
			{ __min[0], __max[1], __min[2] },
			{ __min[0], __min[1], __max[2] }
		};

		AttrBuffer16* vb = AttrBuffer16::s_default_register.insert(s_vtx);

		vb->setBufferType(AttrBuffer16::BUFFER_TYPE_VERTEX);

		static const AttrDescriptor scr[] =
		{
			AttrDescriptor(attr_semantics_position, ATTR_ELEMENT_FLOAT32, 3),
		};

		VectorArrayImm< float, 3 > vtx(__vtx, sizeof(__vtx) / sizeof(__vtx[0]));

		vb->setAttrScript(sizeof(scr) / sizeof(scr[0]), scr);	
		vb->setBulkCopy(vtx);

		attr_buffer = vb;
	}

	if (!attr_buffer->actuate(s_vtx))
	{
		fprintf(stderr, "%s: failed actuating vertex buffer '%s'; bailing out\n", __FUNCTION__, s_vtx.c_str());
		return;
	}

	const AttrDescriptor& desc = attr_buffer->getAttrDescriptor(0);
	const unsigned offset = attr_buffer->getAttrOffset(0);

	const GLint attrLoc = prog->getAttribLocation(desc.semantics);

	if (-1 == attrLoc)
	{
		fprintf(stderr, "%s: missing attribute '%s' from gpu program; bailing out\n", __FUNCTION__, desc.semantics.c_str());
		return;
	}

	const AttrBuffer16* attr_buffei;

	if (0 == (attr_buffei = AttrBuffer16::s_default_register.seek(s_idx)) ||
		attr_buffei->getBufferType() != AttrBuffer16::BUFFER_TYPE_INDEX)
	{
		static const uint16 __idx[12][2] =
		{
			{ 0, 1 },
			{ 0, 2 },
			{ 0, 3 },
			{ 4, 5 },
			{ 4, 6 },
			{ 4, 7 },
			{ 1, 6 },
			{ 2, 7 },
			{ 3, 5 },
			{ 5, 2 },
			{ 6, 3 },
			{ 7, 1 }
		};

		AttrBuffer16* ib = AttrBuffer16::s_default_register.insert(s_idx);

		ib->setBufferType(AttrBuffer16::BUFFER_TYPE_INDEX);

		static const AttrDescriptor scr[] =
		{
			AttrDescriptor(attr_semantics_index, ATTR_ELEMENT_UINT16, 2)
		};

		VectorArrayImm< uint16, 2 > idx(__idx, sizeof(__idx) / sizeof(__idx[0]));

		ib->setAttrScript(sizeof(scr) / sizeof(scr[0]), scr);
		ib->setBulkCopy(idx);

		attr_buffei = ib;
	}

	if (!attr_buffei->actuate(s_idx))
	{
		fprintf(stderr, "%s: failed actuating index buffer '%s'; bailing out\n", __FUNCTION__, s_idx.c_str());
		return;
	}

	const AttrDescriptor& desi = attr_buffei->getAttrDescriptor(0);

	glVertexAttribPointerARB(attrLoc, desc.element_count, GL_FLOAT, GL_FALSE, attr_buffer->getBufferRecSize(), (const GLvoid *) offset);
	glEnableVertexAttribArrayARB(attrLoc);

	glDrawElements(GL_LINES, attr_buffei->getBufferLength() * desi.element_count, GL_UNSIGNED_SHORT, 0);
	glDisableVertexAttribArrayARB(attrLoc);

	if (reportGLError(stderr))
		fprintf(stderr, "error: failed drawing elements with attribute array buffer '%s'\n", s_idx.c_str());
}


void BoxVisual::drawWire()
{
	draw();
}

} // namespace rend

